﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Telerik.Web.UI.Widgets;
using System.Web;
using Microsoft.SharePoint;
using System.IO;
using System.Collections.ObjectModel;
using System.Collections;

namespace ArrowWebParts
{
    class SPFileBrowserContentProvider : FileBrowserContentProvider
    {
        protected bool _isFilterFiles = false;

        #region Constructors
        // constructor must be present when overriding a base content provider
        // you can leave it empty
        public SPFileBrowserContentProvider(HttpContext context, string[] searchPatterns,
            string[] viewPaths, string[] uploadPaths, string[] deletePaths,
            string selectedUrl, string selectedItemTag)
            : base(context, searchPatterns, viewPaths, uploadPaths, deletePaths,
            selectedUrl, selectedItemTag)
        {
        }
        #endregion Constructors

        #region Unsupported Methods
        public override string CreateDirectory(string path, string name)
        {
            throw new NotImplementedException();
        }
        public override string DeleteDirectory(string path)
        {
            throw new NotImplementedException();
        }
        public override string DeleteFile(string path)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Stores an image with the given url and image format
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="url"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public override string StoreBitmap(System.Drawing.Bitmap bitmap, string url, System.Drawing.Imaging.ImageFormat format)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Creates a file item from a HttpPostedFile to the given path with the given name
        /// </summary>
        /// <param name="file"></param>
        /// <param name="path"></param>
        /// <param name="name"></param>
        /// <param name="arguments"></param>
        /// <returns></returns>
        public override string StoreFile(Telerik.Web.UI.UploadedFile file, string path, string name, params string[] arguments)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Gets a read only Stream for accessing the file item with the given url.
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public override System.IO.Stream GetFile(string url)
        {
            // This method will probably not be implemented for arrow, because
            // in Arrow, we're not trying to give the user access to files, they
            // already have that.  we're trying to give them a way to point us
            // to a file, easily and efficiently.

            // url will be site | list | folder | item
            // determine type
            // run open method for type
            // return file stream (items) or null

            throw new NotImplementedException();
        }
        /// <summary>
        /// Get the name of the file with the given url
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public override string GetFileName(string url)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Gets the virtual path of the item with the given url
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public override string GetPath(string url)
        {
            throw new NotImplementedException();
        }
        #endregion Unsupported Methods

        #region FileBrowserContentProvider Overrides
        /// <summary>
        /// Resolves a directory with the given path
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public override DirectoryItem ResolveDirectory(string path)
        {
            return CreateSPDirectoryItem(path, true, _isFilterFiles);
        }
        /// <summary>
        /// Resolves a root directory with the gien path in tree mode
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public override DirectoryItem ResolveRootDirectoryAsTree(string path)
        {
            return CreateSPDirectoryItem(path, true, _isFilterFiles);
        }
        #endregion FileBrowserContentProvider Overrides

        #region Helper Methods
        /// <summary>
        /// Creates a DirectoryItems for the SharePoint object specified by dirFullPath.
        /// The path is tested to determine if it is an SPWeb.  If it is not, then it is
        /// processed as an SPFolder.  If processing as an SPFolder fails, then an empty
        /// DirectoryItem is returned.
        /// </summary>
        /// <param name="absoluteUrl">the absolute URL for the SharePoint object</param>
        /// <param name="isRecurseForWebs">If true and the object specified by absoluteUrl
        /// is determined to be an SPWeb, then subdirectories will be created for each
        /// subweb and document library. Otherwise no such objects will be created.</param>
        /// <param name="isFilterFiles">If true, only folder items are added.</param>
        /// <returns></returns>
        private static DirectoryItem CreateSPDirectoryItem(string absoluteUrl, bool isRecurseForWebs, bool isFilterFiles)
        {
            // File explorer seems to pass call 3 times with path set in ViewPaths.
            // Perhaps it uses same path from ViewPaths as nondirty default for
            // DeletePaths and UpdatePaths...

            DirectoryItem di = CreateEmptyDirectoryItem();
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                // process site
                using (SPSite siteClxn = new SPSite(absoluteUrl))
                {
                    using (SPWeb web = siteClxn.OpenWeb())
                    {
                        if (IsPathWebAbsUrl(absoluteUrl, web)) // this is a website
                            di = CreateSPWebDirectoryItem(web, absoluteUrl, isRecurseForWebs, isFilterFiles);
                        else // this is a lib/folder
                            di = CreateSPFolderDirectoryItem(web, absoluteUrl, true, isFilterFiles);
                    }
                }
            });
            return di;
        }

        /// <summary>
        /// Creates and returns a DirectoryItem for the specified web.
        /// </summary>
        /// <param name="web"></param>
        /// <returns></returns>
        private static DirectoryItem CreateSPWebDirectoryItem(SPWeb web, string parentDir, bool isRecursive, bool isFilterFiles)
        {
            try
            {
                // web info
                SPWebInfo webInfo = CreateSPWebInfo(web);

                // create subdirectories
                List<DirectoryItem> subDirItemList = new List<DirectoryItem>(webInfo.NumSubsites + webInfo.NumDocLibs);

                if (isRecursive)
                {
                    // subsites
                    foreach (string subsiteUrl in webInfo.Subsites)
                        subDirItemList.Add(CreateSPDirectoryItem(subsiteUrl, false, isFilterFiles));

                    // doc libs
                    foreach (SPDocumentLibraryInfo docLibInfo in webInfo.DocLibs)
                        subDirItemList.Add(CreateSPDocumentLibraryDirectoryItem(webInfo.WebUrl, docLibInfo));
                }

                return new DirectoryItem(
                    webInfo.WebTitle,
                    parentDir,
                    webInfo.WebUrl,
                    null,
                    PathPermissions.Read,
                    new FileItem[0],
                    subDirItemList.ToArray());
            }
            catch (Exception ex)
            {
                // log the exception; return empty dir item?
                return CreateEmptyDirectoryItem();
            }
        }

        /// <summary>
        /// Creates a DirectoryItem for the specified SPDocumentLibraryInfo
        /// </summary>
        /// <param name="parentDir">The absolute URL of the SPDocumentLibrary's parent SPWeb.</param>
        /// <param name="docLibInfo"></param>
        /// <returns></returns>
        private static DirectoryItem CreateSPDocumentLibraryDirectoryItem(string parentWebAbsUrl, SPDocumentLibraryInfo docLibInfo)
        {
            DirectoryItem di = new DirectoryItem(
                docLibInfo.Name,
                parentWebAbsUrl,
                docLibInfo.Url,
                null,
                PathPermissions.Read,
                new FileItem[0], // we'll fill these in when the doc lib is explored
                new DirectoryItem[0]);
            return di;
        }

        /// <summary>
        /// Creates a DirectoryItem for the specified document library/folder path (dirFullPath).
        /// </summary>
        /// <param name="web">The parent web of the library/folder</param>
        /// <param name="dirFullPath">the absolute url of the library/folder</param>
        /// <param name="isRecursive">If true, subdirectories are added for each subfolder, and file items are
        /// added for each file.  No subdirectories or files are added if false.</param>
        /// <returns></returns>
        private static DirectoryItem CreateSPFolderDirectoryItem(SPWeb web, string dirFullPath, bool isRecursive, bool isFilterFiles)
        {
            // to test if an SPFolder exists, you have to get it, run an op on it like
            // folder.Exists, then catch the FileNotFoundException
            SPFolder folder = web.GetFolder(dirFullPath);
            try
            {
                if (folder.Exists)
                {
                    SPFolderInfo libFolderInfo = CreateSPFolderInfo(folder, web, dirFullPath, isFilterFiles);

                    // subdirectory and file lists
                    List<DirectoryItem> subdirectories = new List<DirectoryItem>(libFolderInfo.Subfolders.Count);
                    List<FileItem> files = new List<FileItem>(libFolderInfo.Files.Count);

                    // add subdirectories and files if requested
                    if (isRecursive)
                    {
                        // subfolders
                        foreach (SPFolderInfo subfolderInfo in libFolderInfo.Subfolders)
                            subdirectories.Add(CreateSPFolderDirectoryItem(web, subfolderInfo.FullPath, false, isFilterFiles));

                        // files
                        foreach (SPFileInfo fileInfo in libFolderInfo.Files)
                            files.Add(CreateSPFileItem(fileInfo));
                    }

                    // return the library/folder DirectoryItem
                    return new DirectoryItem(
                        libFolderInfo.Name,
                        libFolderInfo.ParentDir,
                        libFolderInfo.FullPath,
                        null,
                        PathPermissions.Read,
                        files.ToArray(),
                        subdirectories.ToArray());

                }
                // if the folder doesn't exist, return an empty directory item
                else return CreateEmptyDirectoryItem();
            }
            catch (FileNotFoundException fnf)
            {
                // folder not found; return empty directory item?
                return CreateEmptyDirectoryItem();
            }
        }

        /// <summary>
        /// <para>Creates an empty DirectoryItem with no files or subdirectories, and 
        /// the following properties:</para>
        /// <para>Name: "NOT FOUND"</para>
        /// <para>Location: string.Empty</para>
        /// <para>Full Path: string.Empty</para>
        /// <para>PathPermissions.Read</para>
        /// <para></para>
        /// </summary>
        /// <returns></returns>
        private static DirectoryItem CreateEmptyDirectoryItem()
        {
            return new DirectoryItem(
                "NOT FOUND",
                string.Empty,
                string.Empty,
                string.Empty,
                PathPermissions.Read,
                new FileItem[] { },
                new DirectoryItem[] { });
        }

        /// <summary>
        /// Creates a FileItem from the specified FileInfo.
        /// </summary>
        /// <param name="fileInfo"></param>
        /// <returns></returns>
        private static FileItem CreateSPFileItem(SPFileInfo fileInfo)
        {
            FileItem fi = new FileItem(
                fileInfo.Name,
                fileInfo.Extension,
                fileInfo.Bytes,
                //fileInfo.ParentDir,
                fileInfo.FullPath,
                fileInfo.FullPath,
                null,
                PathPermissions.Read);
            return fi;
        }

        /// <summary>
        /// Creates an SPWebInfo object from the specified SPWeb.
        /// </summary>
        /// <param name="web"></param>
        /// <returns></returns>
        private static SPWebInfo CreateSPWebInfo(SPWeb web)
        {
            SPWebInfo webInfo = new SPWebInfo();
            webInfo.WebTitle = web.Title;
            webInfo.WebUrl = web.Url;

            // get the subsites
            foreach (SPWeb subweb in web.Webs)
                webInfo.addSubsiteUrl(subweb.Url);

            // get the doc libs?
            foreach (SPList docLib in web.Lists)
            {
                if (docLib is SPDocumentLibrary && !docLib.Hidden)
                {
                    string docLibUrl = string.Format("{0}/{1}", web.Url, ((SPList)docLib).RootFolder.Url);
                    webInfo.addDocLibListing(docLib.Title, docLibUrl);
                }
            }
            return webInfo;
        }

        /// <summary>
        /// Creates a LibFolderInfo 
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="web"></param>
        /// <param name="dirFullPath"></param>
        /// <returns></returns>
        private static SPFolderInfo CreateSPFolderInfo(SPFolder folder, SPWeb web, string dirFullPath, bool isFilterFiles)
        {
            SPFolderInfo libFolderInfo = new SPFolderInfo();
            libFolderInfo.FullPath = dirFullPath;
            libFolderInfo.Name = ArrowUtils.SubstringAfterLastSlash(dirFullPath);
            libFolderInfo.ParentDir = ArrowUtils.SubstringThroughLastSlash(dirFullPath);

            // subfolders
            foreach (SPFolder subFolder in folder.SubFolders)
            {
                if (subFolder != null && subFolder.Exists && subFolder.Item != null)
                {
                    SPFolderInfo subfolderInfo = new SPFolderInfo();
                    subfolderInfo.Name = subFolder.Name;
                    subfolderInfo.FullPath = string.Format("{0}/{1}", web.Url, subFolder.Url);
                    subfolderInfo.ParentDir = libFolderInfo.FullPath;
                    libFolderInfo.addSubfolderInfo(subfolderInfo);
                }
            }

            // files
            if (!isFilterFiles)
            {
                foreach (SPFile file in folder.Files)
                {
                    if (file != null && file.Item != null)
                    {
                        SPFileInfo fileInfo = new SPFileInfo();
                        fileInfo.Name = file.Name;
                        fileInfo.FullPath = string.Format("{0}/{1}", web.Url, file.Url);
                        fileInfo.ParentDir = libFolderInfo.FullPath;
                        if (fileInfo.Name.LastIndexOf('.') != -1)
                            fileInfo.Extension = fileInfo.Name.Substring(fileInfo.Name.LastIndexOf('.') + 1);
                        else
                            fileInfo.Extension = string.Empty;
                        fileInfo.Bytes = file.Length;
                        libFolderInfo.addFileInfo(fileInfo);
                    }
                }
            }

            return libFolderInfo;
        }

        /// <summary>
        /// Indicates if the specified path string is the absolute URL of the
        /// specified web.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="web"></param>
        /// <returns></returns>
        private static bool IsPathWebAbsUrl(string path, SPWeb web)
        {
            return web.Url.ToLower() == path.ToLower();
        }
        #endregion Helper Methods

        #region Utility Classes
        /// <summary>
        /// Directory info for an SPWeb.  This info is used to create a DirectoryItem
        /// for an SPWeb object.
        /// </summary>
        class SPWebInfo
        {
            private List<string> subsiteUrls;
            private SPDocumentLibraryInfoList docLibs;

            /// <summary>
            /// Creates a new web info object.
            /// </summary>
            public SPWebInfo()
            {
                subsiteUrls = new List<string>();
                docLibs = new SPDocumentLibraryInfoList();
            }

            /// <summary>
            /// Title of the web.
            /// </summary>
            public string WebTitle { get; set; }
            /// <summary>
            /// Url of the web.
            /// </summary>
            public string WebUrl { get; set; }
            /// <summary>
            /// The number of subsites in the web site.
            /// </summary>
            public int NumSubsites
            {
                get
                {
                    return subsiteUrls.Count;
                }
            }
            /// <summary>
            /// The number of document libraries in the web site.
            /// </summary>
            public int NumDocLibs
            {
                get
                {
                    return docLibs.Count;
                }
            }

            public List<string> Subsites
            {
                get
                {
                    return subsiteUrls;
                }
            }

            public SPDocumentLibraryInfoList DocLibs
            {
                get
                {
                    return docLibs;
                }
            }
            /// <summary>
            /// Adds the specified web url to the list of subsite urls.
            /// </summary>
            /// <param name="subsiteUrl"></param>
            public void addSubsiteUrl(string subsiteUrl)
            {
                subsiteUrls.Add(subsiteUrl);
            }
            /// <summary>
            /// Adds the specified document library info to the document library listing
            /// contained in the web.
            /// </summary>
            /// <param name="libTitle"></param>
            /// <param name="libUrl"></param>
            public void addDocLibListing(string libTitle, string libUrl)
            {
                docLibs.add(libTitle, libUrl);
            }
        }

        /// <summary>
        /// Directory info for an SPDocumentLibrary.  This is used to create a
        /// DirectoryItem from an SPDocumentLibrary.
        /// </summary>
        class SPDocumentLibraryInfo
        {
            public string Name { get; set; }
            public string Url { get; set; }

            public SPDocumentLibraryInfo(string name, string url)
            {
                Name = name;
                Url = url;
            }
        }

        /// <summary>
        /// An IEnumerable list of SPDocumentLibraryInfo made to be used in a foreach.
        /// </summary>
        class SPDocumentLibraryInfoList : IEnumerable
        {
            private List<SPDocumentLibraryInfo> items;

            /// <summary>
            /// Creates a document library listing.
            /// </summary>
            public SPDocumentLibraryInfoList()
            {
                items = new List<SPDocumentLibraryInfo>();
            }
            /// <summary>
            /// The number of document libraries listed.
            /// </summary>
            public int Count
            {
                get
                {
                    return items.Count;
                }
            }
            /// <summary>
            /// Adds a the specified document library info to the listing.
            /// </summary>
            /// <param name="libTitle"></param>
            /// <param name="libUrl"></param>
            public void add(string libTitle, string libUrl)
            {
                items.Add(new SPDocumentLibraryInfo(libTitle, libUrl));
            }

            #region IEnumerable Members

            public IEnumerator GetEnumerator()
            {
                return (items as IEnumerable).GetEnumerator();
            }

            #endregion
        }

        /// <summary>
        /// Directory info for an SPFolder. This is used to create a
        /// DirectoryItem from an SPFolder object.
        /// </summary>
        class SPFolderInfo
        {
            private SPFolderInfoList subfolders;
            private SPFileInfoList files;

            public SPFolderInfo()
            {
                subfolders = new SPFolderInfoList();
                files = new SPFileInfoList();
            }

            public string Name { get; set; }
            public string ParentDir { get; set; }
            public string FullPath { get; set; }

            public SPFolderInfoList Subfolders
            {
                get
                {
                    return subfolders;
                }
            }

            public SPFileInfoList Files
            {
                get
                {
                    return files;
                }
            }

            public void addSubfolderInfo(SPFolderInfo subfolderInfo)
            {
                subfolders.add(subfolderInfo);
            }

            public void addFileInfo(SPFileInfo fileInfo)
            {
                files.add(fileInfo);
            }
        }

        /// <summary>
        /// An IEnumerable list of SPFolderInfo made to be used in a foreach.
        /// </summary>
        class SPFolderInfoList : IEnumerable
        {
            private List<SPFolderInfo> items;

            public SPFolderInfoList()
            {
                items = new List<SPFolderInfo>();
            }

            public int Count
            {
                get
                {
                    return items.Count;
                }
            }

            public void add(SPFolderInfo libFolderInfo)
            {
                items.Add(libFolderInfo);
            }

            #region IEnumerable Members

            public IEnumerator GetEnumerator()
            {
                return (items as IEnumerable).GetEnumerator();
            }

            #endregion
        }

        /// <summary>
        /// Directory info for an SPFile. This is used to create a
        /// DirectoryItem from an SPFile object.
        /// </summary>
        class SPFileInfo
        {
            private long bytes = 0;

            public string Name { get; set; }
            public string FullPath { get; set; }
            public string ParentDir { get; set; }
            public string Extension { get; set; }
            public long Bytes
            {
                get
                {
                    return bytes;
                }

                set
                {
                    bytes = value;
                }
            }
        }

        /// <summary>
        /// An IEnumerable list of SPFileInfo made to be used in a foreach.
        /// </summary>
        class SPFileInfoList : IEnumerable
        {
            private List<SPFileInfo> items;

            public SPFileInfoList()
            {
                items = new List<SPFileInfo>();
            }

            public int Count
            {
                get
                {
                    return items.Count;
                }
            }

            public void add(SPFileInfo fileInfo)
            {
                items.Add(fileInfo);
            }

            #region IEnumerable Members

            public IEnumerator GetEnumerator()
            {
                return (items as IEnumerable).GetEnumerator();
            }

            #endregion
        }
        #endregion Utility Classes
    }

    class SPFolderBrowserContentProvider : SPFileBrowserContentProvider
    {
        #region Constructors
        // constructor must be present when overriding a base content provider
        // you can leave it empty
        public SPFolderBrowserContentProvider(HttpContext context, string[] searchPatterns,
            string[] viewPaths, string[] uploadPaths, string[] deletePaths,
            string selectedUrl, string selectedItemTag)
            : base(context, searchPatterns, viewPaths, uploadPaths, deletePaths,
            selectedUrl, selectedItemTag)
        {
            _isFilterFiles = true;
        }
        #endregion Constructors
    }
}
